/* Copyright (c) 2004-2024 peigen.info. All rights reserved. */
package info.peigen.hotpot.common.service.base.processor;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.StopWatch;
import cn.hutool.core.util.ObjectUtil;
import com.google.common.collect.Lists;
import info.peigen.hotpot.common.core.result.ListResult;
import info.peigen.hotpot.common.core.result.PageResult;
import info.peigen.hotpot.common.core.result.SingleResult;
import info.peigen.hotpot.common.service.base.*;
import info.peigen.hotpot.common.service.base.event.LogEventHandler;
import info.peigen.hotpot.common.service.base.event.ServiceOrderLogEvent;
import info.peigen.hotpot.common.service.base.event.ServiceResultLogEvent;
import info.peigen.hotpot.common.service.base.processor.post.PostLogEventProcessor;
import info.peigen.hotpot.common.service.base.processor.pre.CheckOrderProcessor;
import info.peigen.hotpot.common.service.base.processor.pre.PreLogEventProcessor;
import info.peigen.hotpot.common.service.base.test.mock.ServiceTestMockContext;
import info.peigen.hotpot.common.service.base.test.mock.ServiceTestMockContextThreadLocal;
import lombok.AccessLevel;
import lombok.experimental.FieldDefaults;
import lombok.extern.slf4j.Slf4j;
import org.noear.solon.annotation.Inject;
import org.noear.solon.core.AppContext;
import org.noear.solon.data.annotation.TranAnno;
import org.noear.solon.data.cache.CacheService;
import org.noear.solon.data.tran.TranPolicy;
import org.noear.solon.data.tran.TranUtils;

import java.util.List;
import java.util.concurrent.TimeUnit;

import static org.noear.solon.data.tran.TranIsolation.read_committed;

/**
 * <b>(AbstractProcessorService)</b>
 * 所有服务层的抽象类,提供最基础的流程支撑
 *
 * @author Peigen
 * @version 1.0.0
 * @since 2019/05/19
 */
@SuppressWarnings({"unused", "rawtypes", "resource", "CallToPrintStackTrace", "unchecked", "ExtractMethodRecommender"})
@Slf4j
@FieldDefaults(level = AccessLevel.PROTECTED)
public abstract class AbstractProcessorService implements ServiceFacade {
    @Inject
    ProfileConfig         profileConfig;
    @Inject
    LogEventHandler       logEventHandler;
    @Inject
    AppContext            context;
    @Inject
    CacheService          cacheService;
    @Inject
    PreLogEventProcessor  preLogEventProcessor;
    @Inject
    CheckOrderProcessor   checkOrderProcessor;
    @Inject
    PostLogEventProcessor postLogEventProcessor;

    /**
     * 领域模型的命令模式执行器
     *
     * @param serviceContext ProcessorServiceContext
     * @param invoke         ProcessorServiceInvoke
     */
    public void execute(ProcessorServiceContext serviceContext, ProcessorServiceInvoke invoke) {
        try {
            StopWatch watch = serviceContext.initWatch();
            // 1. 增加预处理器
            serviceContext.addProcessors(preLogEventProcessor, checkOrderProcessor);
            invoke.addPreProcessor(serviceContext);
            // 2. 增加业务处理器
            invoke.addProcessor(serviceContext);
            // 3. 增加后处理器
            invoke.addPostProcessor(serviceContext);
            serviceContext.addProcessors(postLogEventProcessor);

            // 4. Mock处理器替换
            if (profileConfig.isMock()) {
                watch.start("Mock封装");
                if (ObjectUtil.isNull(ServiceTestMockContextThreadLocal.currentContext())) {
                    new ServiceTestMockContextThreadLocal(ServiceTestMockContext.builder().build());
                }

                // 4.1 获取全部继承于Process的处理器
                List<Processor> beans = context.getBeansOfType(Processor.class);

                // 将被替换的Mock处理器 列表
                List<Processor> mockProcessors = Lists.newArrayList();
                // 将被移除的处理器 列表
                List<String> removeProcessName = Lists.newArrayList();

                // 4.2 获取当前使用的处理器
                List<Processor> processors = serviceContext.getProcessors();
                processors.forEach(processor -> {
                    String processorName = processor.getClass().getSimpleName();
                    beans.stream()
                            // 4.2.1 过滤出包含Mock名的处理器
                            .filter(bean -> bean.getClass().getSimpleName().contains("Mock"))
                            // 4.2.2 把包含Mock名的处理器取出
                            .filter(bean -> bean.getClass().getSimpleName().replaceAll("Mock", "")
                                    .equalsIgnoreCase(processorName)).findAny().ifPresent(bean -> {
                                // 填充需要替换的Mock处理器
                                mockProcessors.add(bean);
                                // 填充需要移除的处理器
                                removeProcessName.add(processorName);
                            });
                });

                // 4.3 从当前使用的处理器中剔除可被Mock替换的处理器
                processors.removeIf(processor -> CollUtil.contains(removeProcessName, processor.getClass()
                        .getSimpleName()));
                if (CollUtil.isNotEmpty(mockProcessors)) {
                    serviceContext.addProcessors(mockProcessors.toArray(new Processor[0]));
                }
                watch.stop();
            }

            final List<Processor> processors = serviceContext.getProcessors();
            // 5.1 自动排序
            // List<Processor> sortedProcessors = processors.stream().distinct().sorted().collect(Collectors.toList());
            // processors.clear();
            // processors.addAll(sortedProcessors);
            // 5.2 打印当前处理器顺序日志
            // sortedProcessors.forEach(processor -> log.info("{}的序号是{}", processor.getClass().getSimpleName(), processor.level()));
            processors.forEach(processor -> log.debug("{}的序号是{}", processor.getClass()
                    .getSimpleName(), processor.level()));
            // 5.3 执行处理器
            executeProcessors(processors, serviceContext);
            log.info(watch.prettyPrint(TimeUnit.MILLISECONDS));
        } catch (Exception e) {
            invoke.handleException(e, serviceContext);
        }

    }

    /**
     * 领域模型的查询模式执行器
     *
     * @param serviceContext 上下文
     * @param query          查询接口
     */
    @SuppressWarnings("rawtypes")
    public void query(ServiceQueryContext serviceContext, ServiceQuery query) {

        // 0. 打印[开始]日志
        ServiceOrderBase     order    = serviceContext.getOrder();
        ServiceOrderLogEvent logEvent = ServiceOrderLogEvent.builder().order(order).build();
        logEventHandler.eventPost(logEvent);

        try {
            // 1. 校验入参
            query.checkOrder(serviceContext);

            // 2. 服务调用
            query.query(serviceContext);

            // 3. 组装返回对象
            query.convert(serviceContext);

            // 4. 打印[结果]日志
            ServiceResultLogEvent resultLogEvent = ServiceResultLogEvent.builder().partnerId(order.getPartnerId())
                    .build();

            // 4.1 组装返回对象
            // 4.1.1 组装无返回对象结果
            if (serviceContext.getResult() != null) {
                resultLogEvent.setResultBase(serviceContext.getResult());
            }
            // 4.1.2 组装单个返回对象
            if (serviceContext.getResult() instanceof SingleResult) {
                resultLogEvent.setSingleResult((SingleResult<?>) serviceContext.getResult());
            }
            // 4.1.3 组装集合返回对象
            if (serviceContext.getResult() instanceof ListResult) {
                resultLogEvent.setListResult((ListResult<?>) serviceContext.getResult());
            }
            // 4.1.4 组装分页返回对象
            if (serviceContext.getResult() instanceof PageResult) {
                resultLogEvent.setPageResult((PageResult<?>) serviceContext.getResult());
            }
            logEventHandler.eventPost(resultLogEvent);
        } catch (Exception e) {
            query.handleException(e, serviceContext);
        }
    }

    protected String concatDetail(String transRejectCode, String transStatusDescription) {
        return "渠道返回:code=" + transRejectCode + ",desc=" + transStatusDescription;
    }

    private void executeProcessors(List<Processor> processors, ProcessorServiceContext serviceContext) {
        StopWatch watch = serviceContext.getWatch();

        try {
            TranUtils.execute(new TranAnno().isolation(read_committed).policy(TranPolicy.required),
                    () -> processors.stream().filter(ObjectUtil::isNotNull).forEach(processor -> {
                        watch.start("执行处理器:" + processor.getClass()
                                .getSimpleName() + ",处理器Level=" + processor.level());
                        processor.execute(serviceContext);
                        watch.stop();
                    }));
        } catch (Throwable e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage(), e);
        }
    }

}